package hello;

/**
 * Created with IntelliJIDEA.
 * Description:
 * User: ASUS
 * Date: 2025/9/2
 * Time: 9:51
 */
import java.lang.reflect.Method; // 导入 Method 类，它是反射API的一部分，用于描述和操作类中的方法。

// 定义一个简单的 Person 类，作为我们反射操作的目标。
class Person {
    // 1. 私有字段 (Private Field)
    // 封装是面向对象的核心原则之一，通常外部无法直接访问私有字段。
    private String name;

    // 2. 公共构造函数 (Public Constructor)
    // 用于创建 Person 对象的入口。
    public Person(String name) {
        this.name = name;
    }

    // 3. 公共方法 (Public Method)
    // 外部可以直接调用的方法。
    public void sayHello() {
        System.out.println("Hello, I am " + this.name);
    }

    // 4. 私有方法 (Private Method)
    // 按照访问控制权限，这个方法只能在 Person 类内部被调用。
    private void whisper(String message) {
        System.out.println("(whispering) " + message);
    }
}

public class ReflectionDemo {
    public static void main(String[] args) throws Exception { // 为了简化代码，直接抛出异常，实际项目中应具体捕获。

        // -------------------------------------------------------------------------
        // 知识点一: 获取 Class 对象
        // Class 对象是反射的起点，它代表了一个类的“图纸”。获取它有三种常见方式：
        // 1. Class.forName("全限定类名"): 最常用，尤其是在类名以字符串形式提供时（如配置文件）。
        // 2. 类名.class: 编译时就已经确定，最安全，性能最好。例如 Person.class。
        // 3. 对象.getClass(): 从一个已有的对象实例获取。例如 new Person("").getClass()。
        // -------------------------------------------------------------------------

        // 步骤 1: 获取 Person 类的 Class 对象
        // 这里使用 forName 方法，参数是类的全限定名（包名+类名）。因为 Person 在同一个包下，所以可以直接写 "Person"。
        Class<?> personClass = Class.forName("Person");

        // -------------------------------------------------------------------------
        // 知识点二: 通过反射创建实例
        // 获取 Class 对象后，可以通过它来创建这个类的实例，相当于执行了 new Person(...)。
        // -------------------------------------------------------------------------

        // 步骤 2: 创建 Person 类的实例
        // a. 首先，获取与参数类型匹配的构造函数。getConstructor() 只能获取 public 构造函数。
        //    这里要获取的是 public Person(String name) 这个构造器，所以参数是 String.class。
        // b. 然后，调用构造函数的 newInstance() 方法，并传入实际的参数 ("Alice") 来创建对象。
        Object personInstance = personClass.getConstructor(String.class).newInstance("Alice");

        // -------------------------------------------------------------------------
        // 知识点三: 获取并调用公共方法
        // -------------------------------------------------------------------------

        // 步骤 3: 获取名为 "sayHello" 的公共方法
        // 使用 getMethod(方法名, 参数类型...) 来获取一个 public 方法。
        // 因为 sayHello() 没有参数，所以第二个参数为空。
        Method sayHelloMethod = personClass.getMethod("sayHello");

        // 步骤 4: 调用该方法
        // Method 对象的 invoke(对象实例, 方法参数...) 方法用于执行这个方法。
        // 第一个参数 personInstance 指明了是在哪个对象上调用此方法。
        // 因为 sayHello() 没有参数，所以没有更多参数。
        sayHelloMethod.invoke(personInstance); // 控制台输出: Hello, I am Alice

        // -------------------------------------------------------------------------
        // 知识点四: 获取并调用私有方法 (核心)
        // 反射的强大之处在于它可以突破访问权限的限制。
        // -------------------------------------------------------------------------

        // 步骤 5: 获取名为 "whisper" 的私有方法
        // getMethod() 只能获取 public 方法。要获取私有、保护或包级私有方法，必须使用 getDeclaredMethod()。
        // getDeclaredMethod() 只查找在当前类中直接声明的方法，不包括父类继承的方法。
        // 这里我们要找的是 private void whisper(String message)，所以参数是 "whisper" 和 String.class。
        Method whisperMethod = personClass.getDeclaredMethod("whisper", String.class);

        // 步骤 6: 使私有方法可以被访问
        // 默认情况下，即使获取了私有方法的 Method 对象，也是无法调用的，会抛出 IllegalAccessException。
        // setAccessible(true) 是关键，它会关闭 Java 语言的访问权限检查，允许我们调用私有方法。
        // 这也被称为“暴力反射”。
        whisperMethod.setAccessible(true);

        // 步骤 7: 调用私有方法
        // 调用方式与公共方法一样，使用 invoke()。
        // 第一个参数是对象实例 personInstance。
        // 第二个参数 "I have a secret." 是要传递给 whisper 方法的实参。
        whisperMethod.invoke(personInstance, "I have a secret."); // 控制台输出: (whispering) I have a secret.
    }
}

